package se.jocke.nb.log.internal;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import se.jocke.nb.log.LineListener;

/**
 *
 * @author jocke
 */
public class Seeker implements Runnable {

    private final File file;

    private final LineListener listener;

    private final int max;

    private final boolean fromTop;

    private final RandomAccessFile randomAccessFile;

    private volatile boolean running = false;

    public Seeker(File file, LineListener listener, int max, boolean fromTop) throws FileNotFoundException {
        this.file = file;
        this.listener = listener;
        this.max = max;
        this.fromTop = fromTop;
        this.randomAccessFile = new RandomAccessFile(file, "r");
    }

    void setRunning(boolean b) {
        this.running = b;
    }

    @Override
    public void run() {

        if (fromTop) {
            seekFromTop();

        } else {
            seekFromBottom();
        }

    }

    private void seekFromTop() {
        String line = null;
        int lineNum = 0;
        try {
            while ((line = randomAccessFile.readLine()) != null && lineNum < max && running) {
                if (listener.matches(line)) {
                    lineNum++;
                    listener.onMatch(line, lineNum);
                }
            }
        } catch (IOException ex) {
            listener.onIOException(ex);
        }

    }

    private void seekFromBottom() {
        String line = null;
        int lineNum = 0;
        try {
            randomAccessFile.seek(file.length());
        } catch (IOException ex) {
            listener.onIOException(ex);
        }
        while ((line = readLineReverse()) != null && lineNum < max && running) {
            if (listener.matches(line)) {
                lineNum++;
                listener.onMatch(line, lineNum);
            }
        }
    }

    private String readLineReverse() {
        StringBuilder input = new StringBuilder();
        try {
            int c = -1;
            boolean eol = false;

            long seek = randomAccessFile.getFilePointer();

            while (!eol && seek >= 2) {
                seek = (seek - 2);
                randomAccessFile.seek(seek);
                switch (c = randomAccessFile.read()) {
                    case -1:
                    case '\n':
                        eol = true;
                        break;
                    case '\r':
                        eol = true;
                        long cur = randomAccessFile.getFilePointer();
                        if ((randomAccessFile.read()) != '\n') {
                            randomAccessFile.seek(cur);
                        }
                        break;
                    default:
                        input.append((char) c);
                        break;
                }
            }

            if ((c == -1) && (input.length() == 0)) {
                return null;
            }

        } catch (IOException ex) {
            listener.onIOException(ex);
            return null;
        }

        return input.toString();
    }

}
